import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class CTRMainJFrame extends javax.swing.JFrame {
    //声明变量 以在内部类中也能够被调用
    JTextArea jta=null;
    JScrollPane jsp=null;
    JPanel jp1=null;
    JTextField jtf=null;
    JButton jb=null;
    DatagramSocket udpsocket;

    DatagramSocket SendSocket=null;
    DatagramSocket ReceiveSocket=null;
    InetAddress address= null;
    int ReceivePort=0;
    String ReceivePortStr;
    String nameCopy;
    int portCopy=0;

    //保存聊天记录
    FileWriter writer=null;

    //经过实践发现 不放在外面 就不能在内部类中访问

    //public CTRMainJFrame(String ip,int port,String name) {
    public CTRMainJFrame(InetAddress addressin,int port,String name) {

        //初始化各组件 复制自老师给的示例
        jta=new JTextArea();
        jsp=new JScrollPane(jta);
        jp1=new JPanel();
        jtf=new JTextField(65);
        jb=new JButton("发送");

        //创建两个Socket，一个用于发送数据，另一个用于接收从服务器发来的，其他人的消息
        try {
            SendSocket = new DatagramSocket();
        } catch (SocketException e) {
            throw new RuntimeException(e);
        }

        try {
            ReceiveSocket = new DatagramSocket();
        } catch (SocketException e) {
            throw new RuntimeException(e);
        }

        //初始化一个“接收端口” 把发送端口和接收端口分开 来避免冲突
        ReceivePort = ReceiveSocket.getLocalPort();
        ReceivePortStr=String.format("%05d",ReceivePort);//把端口转成字符串 控制在5位（端口最长是5位） 放在每条发出去的消息前

        nameCopy=name;//把名字Copy一遍 存储在在外面声明的变量里 用来给内部类调用
        portCopy=port;//把端口Copy一遍 作用同上

        //把ip地址从string转成InetAddress 以便调用
//        try {
//            address = InetAddress.getByName(ip);
//            //address = InetAddress.getLocalHost();
//        } catch (UnknownHostException e) {
//            throw new RuntimeException(e);
//        }
        address=addressin;



        //用来存放接收的消息的byte数组和Packet
        byte[] getbuf = new byte[1024 * 64];//既要发 也要收
        DatagramPacket getpacket = new DatagramPacket(getbuf, getbuf.length);

        //成功连接服务端后 发送第一条消息 目的有二：一是可以告知聊天室内的其他人 二是只有发送第一条消息后才能从服务端接收消息
        Sendmsg(nameCopy+" 成功登录.");




        //用来实现按下发送按钮后的功能
        jb.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                String msg= jtf.getText();//获取文本框的内容
                jtf.setText("");//获取后清除文本框的内容

                Sendmsg(msg);//发送消息msg
            }
        });

        //用来实现回车也能发消息 方便了打字的人手不用离开键盘
        jtf.addKeyListener(new KeyAdapter() {
            public void keyPressed(KeyEvent e) {
                if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                    String msg= jtf.getText();//获取文本框的内容
                    jtf.setText("");//获取后清除文本框的内容

                    Sendmsg(msg);//发送消息msg
                }
            }
        });

        //创建文件对象 用于读取和写入聊天记录
        String filepath="ChatLog"+File.separator+name+".log";

        //获得目录对象 如果不存在则创建
        File Dir=new File("ChatLog");
        if (!Dir.exists()) {
            if (!Dir.mkdir()) {
                return;
            }
        }

        //获得存储聊天记录的文件对象 如果不存在则创建
        File chatlogfile=new File(filepath);
        try{
            if(!(chatlogfile.exists()))
            {
                chatlogfile.createNewFile();
            }
        }
        catch (IOException ee)
        {
            ee.printStackTrace();
        }
        //读入以往聊天记录
        //参考自 https://www.cnblogs.com/jeesezhang/p/4517868.html
        BufferedReader reader=null;
        try{
            reader=new BufferedReader(new FileReader(chatlogfile));//初始化BufferedReader对象 用来读取存储的聊天记录
            String line=null;//初始化用来存放读取的内容的字符串
            while((line=reader.readLine())!=null)
            {
                jta.append(line+"\n");
            }
        }
        catch(IOException ee)
        {
            ee.printStackTrace();
        }
        finally
        {
            if(reader!=null)
                try{
                    reader.close();
                }catch(IOException e){
                    e.printStackTrace();
                }
        }

        //初始化一个FileWriter对象 用来在下面的线程中写入聊天记录
        try{
            writer=new FileWriter(filepath,true);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        //另一个线程 用来接收和写入聊天记录
        Thread threadReceive = new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                boolean firstCheck=false;//第一次的标记




                while(true) {
                    try {
                        if(!firstCheck)//如果是第一次 看能不能在2秒内成功收到第一条信息 收不到说明连接失败
                        {
                            ReceiveSocket.setSoTimeout(2000);
                            firstCheck=true;
                        }
                        else
                        {
                            ReceiveSocket.setSoTimeout(0);
                        }
                        ReceiveSocket.receive(getpacket);
                    } catch (IOException e) {

                        JumpWin("连接失败，请检查IP和端口后重试。");
                        System.exit(1);
                        //throw new RuntimeException(e);
                    }
                    String receivedmsg = new String(getpacket.getData(), 0, getpacket.getLength(), StandardCharsets.UTF_8);//从Packet到String

                    //这部分是用来检查看能不能收到第一条语句 即登陆成功语句 如果不能说明IP或端口可能有误
                    //没什么用 不要了
//                    if(!firstCheck)
//                    {
//                        if(!receivedmsg.substring(10).equals(nameCopy + ":" + nameCopy + " 成功登录."))
//                        {
//                            System.out.println(receivedmsg.substring(10));
//                            System.out.println("语句对比");
//                            System.out.println(nameCopy + ":" + nameCopy + " 成功登录.");
//                            JumpWin("登陆失败，请检查IP和端口后重试。");
//                            System.exit(1);
//                        }
//                        firstCheck=true;
//
//                    }



                    System.out.println(receivedmsg);//测试用的输出
                    jta.append(receivedmsg+"\n");//把接收到的消息放到文本显示区域

                    //把接收到的消息写入聊天记录存储文件
                    try {
                        writer.write(receivedmsg+"\n");
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }


                }

            }
        });
        //启动上面写的这个线程
        threadReceive.start();

        //这部分和老师给的样例一致，是直接复制过来的，除了窗口大小略有调整外
        jp1.add(jtf);
        jp1.add(jb);

        this.add(jsp);
        this.add(jp1, BorderLayout.SOUTH);
        this.setSize(900,600);

        //在标题处显示昵称
        this.setTitle("mCAU聊天室  您当前的昵称是： "+name+"      软件版本："+Global.ver);

        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //窗体关闭时 需要把FileWriter关闭 来把数据实际写入文件中 并且还需要手动关闭一下程序
        this.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                try {
                    writer.close();
                } catch (IOException eee) {
                    throw new RuntimeException(eee);
                }
                FrameClose();//调用了System.exit(0);
            }
        });


        this.setLocationRelativeTo(null);
        this.setVisible(true);


    }

    //把发送消息的部分提取出来一个方法
    //形式参数仅为需要发送的消息 调用处比较灵活简洁
    private void Sendmsg(String msg) {

        LocalDateTime now = LocalDateTime.now();//获取当前时间
        DateTimeFormatter sdf = DateTimeFormatter.ofPattern("HH:mm:ss");//把时间格式化
        String date = now.format(sdf);//再把时间转化成字符串

        //处理文本 使得每条发出去的消息都带有 客户端接受数据的Socket的端口 发送的时间 发送的名字（之所以用nameCopy是因为name不是在外面定义的）和消息本身

        String sendmsg = ReceivePortStr+"[" + date + "]" + nameCopy + ":" + msg;//这里的冒号不能用中文字符 很奇怪 用了就变成 锛�

        byte[] buf = sendmsg.getBytes(StandardCharsets.UTF_8);//使用UTF-8编码 以便发送中文
        DatagramPacket packet = new DatagramPacket(buf, buf.length, address, portCopy);//初始化packet
        try {
            SendSocket.send(packet);
        } catch (IOException ee) {
            throw new RuntimeException(ee);
        }
    }

    //用来关闭程序
    void FrameClose()
    {
        System.exit(0);
    }

    //弹窗
    void JumpWin(String msg)
    {
        JOptionPane.showMessageDialog(null, msg, "提示", JOptionPane.INFORMATION_MESSAGE);
    }
}


